﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using DimensionsHelper.Common.Contracts;
using DimensionsHelper.Common.Utils;

namespace DimensionsHelper.Common.Sql;

[Serializable]
public class MetadataCodeMapElement : INamedObject
{
    private readonly StringBuilder _fieldNames;
    private readonly List<string> _fieldNameStorage;
    private readonly HashSet<string> _fields;
    private readonly Dictionary<string, string> _mapping;
    private readonly Dictionary<string, string> _subFieldsMapping;

    private readonly RegexManager _valuesRegex;
    private MetadataCodeMap? _codeMap;
    private EventHandler<FieldChangedEventArgs>? _fieldChangedHandler;

    private bool _isValueExprDirty = true;

    private MetadataValueType _valueType = MetadataValueType.Categorical;

    public MetadataCodeMapElement()
    {
        _valuesRegex = new RegexManager
        {
            Exactly = true,
            IgnoreCase = true
        };

        _valuesRegex.FormatElement += OnValuesRegexElementFormatting;

        _fieldNameStorage = [];
        _fields = [];
        _fieldNames = new StringBuilder();
        _subFieldsMapping = new Dictionary<string, string>();
        _mapping = new Dictionary<string, string>();
    }

    [JsonIgnore]
    public MetadataCodeMap? CodeMap
    {
        get => _codeMap;
        set
        {
            _codeMap = value;

            _valuesRegex.Splitter = "";

            if (_codeMap != null)
            {
                _valuesRegex.Splitter = _codeMap.MultipleSplitter;
            }
        }
    }

    /// <summary>
    ///     获取或指定字段码号使用的前缀码号描述。
    /// </summary>
    public string? Label { get; set; }

    /// <summary>
    ///     指定当前字段是否为整数类型，如果此属性为true，生成脚本时将忽略<see cref="Mapping" />并不添加双引号。
    /// </summary>
    public bool IsLong
    {
        get => _valueType == MetadataValueType.Long;

        set
        {
            if (value)
            {
                _valueType = MetadataValueType.Long;
            }
            else if (_valueType == MetadataValueType.Long)
            {
                _valueType = MetadataValueType.Categorical;
            }
        }
    }


    public bool IsDouble
    {
        get => _valueType == MetadataValueType.Double;

        set
        {
            if (value)
            {
                _valueType = MetadataValueType.Double;
            }
            else if (_valueType == MetadataValueType.Double)
            {
                _valueType = MetadataValueType.Categorical;
            }
        }
    }

    /// <summary>
    ///     指定当前字段是否为字符串类型，如果此属性为true，生成脚本时将忽略<see cref="Mapping" />并添加双引号。
    /// </summary>
    public bool IsText
    {
        get => _valueType == MetadataValueType.Text;

        set
        {
            if (value)
            {
                _valueType = MetadataValueType.Text;
            }
            else if (_valueType == MetadataValueType.Text)
            {
                _valueType = MetadataValueType.Categorical;
            }
        }
    }

    /// <summary>
    ///     使用此列表的字段名列表
    /// </summary>
    /// <remarks>
    ///     注意，手动创建对象赋值时，不要使用此属性返回值的List对象添加，必须要将存有数据的IList对象赋值给此属性。
    /// </remarks>
    public IList<string> Fields
    {
        get => _fieldNameStorage;

        set
        {
            _fields.Clear();
            _fieldNames.Clear();

            foreach (string f in value)
            {
                AddField(f);
            }
        }
    }

    /// <summary>
    ///     下级字段映射表，如果此列表不为空，读取表格数据时将从第3行开始，否则，从第2行开始。
    ///     最终此映射表中的值将拼接在<see cref="Fields" />后，因此，此表中的映射值需要配置成所需的全部字符，包括前后的符号。
    /// </summary>
    /// <remarks>
    ///     注意，手动创建对象赋值时，不要使用此属性返回值的List对象添加，必须要将存有数据的IList对象赋值给此属性。
    /// </remarks>
    public IDictionary<string, string> SubFieldsMapping
    {
        get => _subFieldsMapping;

        set
        {
            _subFieldsMapping.Clear();

            foreach (var pair in value)
            {
                _subFieldsMapping.TryAdd(pair.Key.ToLower(), pair.Value);
            }
        }
    }

    /// <summary>
    ///     当前码表，key为描述，value为配置的码表值
    /// </summary>
    /// <remarks>
    ///     注意，手动创建对象赋值时，不要使用此属性返回值的List对象添加，必须要将存有数据的IList对象赋值给此属性。
    /// </remarks>
    public IDictionary<string, string> Mapping
    {
        get => _mapping;

        set
        {
            _mapping.Clear();

            foreach (KeyValuePair<string, string> pair in value)
            {
                _mapping.TryAdd(pair.Key, pair.Value);
            }

            RefreshValuesRegex(true);
        }
    }

    /// <summary>
    ///     当前已配置的字段名列表，中间以逗号分隔
    /// </summary>
    public string Name => _fieldNames.ToString();

    private void RefreshValuesRegex(bool force = false)
    {
        if (!_isValueExprDirty && !force)
        {
            return;
        }

        _valuesRegex.Clear();

        foreach (string key in _mapping.Keys)
        {
            _valuesRegex.Append(key);
        }

        _isValueExprDirty = false;
    }
    

    private void OnValuesRegexElementFormatting(object? sender, RegexElementFormatArgs args)
    {
        if (args.Texts.Count > 0)
        {
            args.Text = string.Join("|", args.Texts.Select(StringHelper.EscapeRegExprExceptDotStarQuestion));
        }
        else
        {
            args.Text = StringHelper.EscapeRegExprExceptDotStarQuestion(args.Text);
        }

        args.Text =
            $"(?:(?i)(?:{(CodeMap == null || string.IsNullOrEmpty(CodeMap.MultipleSplitter) ? "^" : $"^|(?<={CodeMap.MultipleSplitter})")})({args.Text})(?:{(CodeMap == null || string.IsNullOrEmpty(CodeMap.MultipleSplitter) ? "$" : $"$|(?={CodeMap.MultipleSplitter})")}))";
        args.Handled = true;
    }

    /// <summary>
    ///     <see cref="Fields" />中的字段发生变化时触发的事件。
    /// </summary>
    public event EventHandler<FieldChangedEventArgs> FieldChanged
    {
        add => _fieldChangedHandler += value;
        remove => _fieldChangedHandler -= value;
    }

    private void OnFieldChanged(string fieldName, FieldChangeType type)
    {
        _fieldChangedHandler?.Invoke(this, new FieldChangedEventArgs(fieldName, type));
    }

    /// <summary>
    ///     添加指定的字段名
    /// </summary>
    /// <param name="fieldName"></param>
    public void AddField(string fieldName)
    {
        string lower = fieldName.ToLower().Trim();

        if (!_fields.Add(lower))
        {
            return;
        }

        if (_fieldNames.Length > 0)
        {
            _fieldNames.Append(',');
        }

        _fieldNames.Append(lower);
        _fieldNameStorage.Add(fieldName);
        OnFieldChanged(fieldName, FieldChangeType.Add);
    }

    /// <summary>
    ///     检查给定名称是否在已有的下级字段映射表中，并返回映射后的值。
    /// </summary>
    /// <param name="name">给定名称</param>
    /// <returns>如果给定值在映射表中，返回映射后的值，否则，返回传入的原始名称</returns>
    public string GetSubField(string name)
    {
        return _subFieldsMapping.TryGetValue(name.ToLower(), out var result) ? result : name;
    }

    /// <summary>
    ///     判定给定名称是否在当前列表中，不区分大小写
    /// </summary>
    /// <param name="fieldName"></param>
    /// <returns></returns>
    public bool FieldExists(string fieldName)
    {
        return _fields.Contains(fieldName.ToLower());
    }


    /// <summary>
    ///     检查Label配置并添加码号前缀，并返回完整的码号。
    /// </summary>
    /// <param name="code">码号字符串</param>
    /// <param name="appendCurly">是否在外部添加花括号</param>
    /// <returns>格式化后的码号</returns>
    public string GetCode(string code, bool appendCurly = true)
    {
        string prefix = "";

        if (Label == null)
        {
            if (CodeMap != null)
            {
                return CodeMap.GetCode(code, appendCurly);
            }
        }
        else
        {
            prefix = Label;
        }

        string res;

        if (!string.IsNullOrEmpty(prefix))
        {
            res = code;
        }
        else
        {
            res = prefix + code.Replace(" ", "").Replace(",", "," + prefix);
        }

        if (appendCurly)
        {
            return "{" + res + "}";
        }

        return res;
    }

    private string GetMatchedMappingCode(Match match, out string original)
    {
        original = string.Empty;
            
        for (int i = 1; i < match.Groups.Count; i++)
        {
            Group group = match.Groups[i];

            if (!group.Success)
            {
                continue;
            }

            original = _valuesRegex.GetOriginalAt(int.Parse(_valuesRegex.GroupNameFromNumber(i)) - 1);
            return Mapping[original];
        }

        return string.Empty;
    }

    /// <summary>
    ///     依据给定的字符串，构建最终的码号字符。
    ///     如果是单值字符，只检查数据类型和码号标签；
    ///     如果是多值字符，需要检查所有匹配成功的字符，每个字符用','分隔。
    /// </summary>
    /// <param name="text">需要查找的字符</param>
    /// <param name="finalCode">进行替换后的字符</param>
    /// <param name="originalValues">原始值列表</param>
    public bool TryGetMappingCode(string text, out string finalCode, out List<string> originalValues)
    {
        RefreshValuesRegex();

        finalCode = null!;
        originalValues = [];

        var final = new StringBuilder();
        MatchCollection matches = _valuesRegex.Matches(text);

        foreach (Match match in matches.Cast<Match>())
        {
            if (!match.Success)
            {
                continue;
            }

            string code = GetMatchedMappingCode(match, out string original);

            if (!string.IsNullOrEmpty(original))
            {
                originalValues.Add(original);
            }

            if (string.IsNullOrEmpty(code))
            {
                continue;
            }

            if (final.Length > 0)
            {
                final.Append(',');
            }

            final.Append(code);
        }

        if (final.Length <= 0)
        {
            return false;
        }

        finalCode = final.ToString();
        return true;
    }

    public static MetadataCodeMapElement FromArray(MetadataCodeMap codeMap, IList<string> fields,
        IList<string> values)
    {
        var element = new MetadataCodeMapElement
        {
            CodeMap = codeMap,
            _valuesRegex =
            {
                Splitter = codeMap.MultipleSplitter
            }
        };

        foreach (string field in fields)
        {
            element.AddField(field);
        }

        for (int i = 0; i < values.Count; i++)
        {
            string str = values[i];

            if (element.Mapping.ContainsKey(str))
            {
                throw new Exception($"字符串值'{values[i]}'重复，此值不区分大小写。");
            }

            element.Mapping.Add(str, (i + 1).ToString());
            element._valuesRegex.Append(str);
        }

        return element;
    }

    private enum MetadataValueType
    {
        Long,
        Text,
        Double,
        Categorical
    }
}